package cn.bigfire.wzdj.service;

import cn.bigfire.wzdj.action.WZDJCaller;
import cn.bigfire.wzdj.action.WZDJSender;
import cn.bigfire.wzdj.frame.WZDJMainFrame;
import cn.bigfire.wzdj.net.WZDJPackWSDecoder;
import cn.bigfire.wzdj.net.WZDJPackWSEncoder;
import cn.bigfire.wzdj.net.WZDJWSClientHandler;
import cn.bigfire.wzdj.pojo.httpVo.WZDJLocalItem;
import cn.bigfire.wzdj.pojo.httpVo.WxCheckQrRes;
import cn.bigfire.wzdj.pojo.httpVo.WxCheckQrScanData;
import cn.bigfire.wzdj.pojo.httpVo.WxCheckQrScanOkData;
import cn.bigfire.wzdj.pojo.srcVo.Wzdj;
import cn.bigfire.wzdj.pojo.vo.*;
import cn.bigfire.wzdj.util.Consts;
import cn.bigfire.wzdj.util.DateUtils;
import cn.bigfire.wzdj.util.Stores;
import cn.bigfire.wzdj.util.WxApi;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.stream.ChunkedWriteHandler;
import lombok.extern.slf4j.Slf4j;

import javax.swing.*;
import java.net.URI;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

@Slf4j
public class WZDJService {

    //好友上线 更新好友列表
    public static void updateFriendStatus(Wzdj.RpcClientFriendOnlineStatus rpcClientFriendOnlineStatus) {
        Integer frienduid = rpcClientFriendOnlineStatus.getFrienduid()!= null ? rpcClientFriendOnlineStatus.getFrienduid() : 0;
        Integer status = rpcClientFriendOnlineStatus.getStatus()!= null ? rpcClientFriendOnlineStatus.getStatus() : 0;
        if (frienduid > 0){
            List<Wzdj.FriendInfo> myFriendList = Stores.getInstance().getMyFriendList();
            for (Wzdj.FriendInfo friendInfo : myFriendList) {
                if (frienduid.equals(friendInfo.getUserinfo().getUid())) {
                    friendInfo.setStatus(status);
                }
            }
            Stores.getInstance().setMyFriendList(myFriendList);//内存值回写
        }
    }
    public static void showFriends() {
        if (Stores.getInstance().getHomeChannel() == null) {
            WZDJMainFrame.consoleTextArea.append("请先登录，并连接服务器" + "\n");
            return;
        }
        List<Wzdj.FriendInfo> beApplyList = Stores.getInstance().getBeApplyList();
        List<Wzdj.FriendInfo> myFriendList = Stores.getInstance().getMyFriendList();
        if (beApplyList.isEmpty() && myFriendList.isEmpty()){
            return;
        }
        //先根据状态排序，再根据时间排序
        myFriendList.sort((o1, o2) -> {
            if (Objects.equals(o1.getStatus(), o2.getStatus())){
                long cha = o1.getTs() - o2.getTs();
                return (int) cha;
            } else {
                return o1.getStatus() - o2.getStatus();
            }
        });
        StringBuilder sb = new StringBuilder("好友申请个数:" + beApplyList.size() + ",好友个数:" + myFriendList.size()).append("\n");
        for (Wzdj.FriendInfo friendInfo : myFriendList) {
            Wzdj.BasicInfo userinfo = friendInfo.getUserinfo()!= null ? friendInfo.getUserinfo() : new Wzdj.BasicInfo();
            String uBase = WZDJService.getUBase(userinfo);
            int status = friendInfo.getStatus() != null ? friendInfo.getStatus() : 0;
            long ts = friendInfo.getTs() != null ? friendInfo.getTs() : 0;//最后一次离线时间
            long now = System.currentTimeMillis()/1000;//当前时间
            long cha = now - ts;//差值
            String offTime = DateUtils.secondToTime(cha);
            String offLineStr = status == 1 ? "在线" : "离线(" + offTime + ")";
            sb.append(uBase).append(" ").append(offLineStr).append("\n");
        }
        log.info(sb.toString());
        WZDJMainFrame.consoleTextArea.append(sb.toString());
    }

    //清理一个月不在线的好友
    public static void clearFriendsByMonth() {
        int choice = JOptionPane.showConfirmDialog(Stores.getWzdjMainFrame(), "此操作会删除你的好友列表中30天内不在线的老玩家，确认操作吗？", "确认对话框",
                JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE);
        if (choice != JOptionPane.YES_OPTION){
            //没点确认就取消了
            return;
        }
        if (Stores.getInstance().getHomeChannel() == null) {
            WZDJMainFrame.consoleTextArea.append("请先登录，并连接服务器" + "\n");
            return;
        }

        List<Wzdj.FriendInfo> myFriendList = Stores.getInstance().getMyFriendList();
        int count = 0;
        for (Wzdj.FriendInfo friendInfo : myFriendList) {
            Wzdj.BasicInfo userinfo = friendInfo.getUserinfo()!= null ? friendInfo.getUserinfo() : new Wzdj.BasicInfo();
            int status = friendInfo.getStatus() != null ? friendInfo.getStatus() : 0;
            long ts = friendInfo.getTs() != null ? friendInfo.getTs() : 0;//最后一次离线时间
            long now = System.currentTimeMillis()/1000;//当前时间
            long cha = now - ts;//差值
            long mothTS = 30 * 24 * 60 * 60;//一个月描述
            //一个月不在线的好友直接删除掉
            if (status == 0 && cha > mothTS) {
                count++;
                WZDJSender.rpcServerFriendDel(userinfo.getUid());
            }
        }
        WZDJMainFrame.consoleTextArea.append("共有" + count +"个好友一个月没上线，已删除被");
    }


    //进入战斗服 2个用户信息
    public static void enterFightClientFightPlayerList(Channel channel, Wzdj.RpcClientFightPlayerList rpcClientFightPlayerList) {
        for (Wzdj.FightPlayerData player : rpcClientFightPlayerList.getPlayers()) {
            if (Objects.equals(player.getUserinfo().getUid(), Stores.getInstance().getBasicInfo().getUid())) {
                Stores.getInstance().setMyUid(player.getUserinfo().getUid());//进入战斗赋值 uid信息
                Stores.getInstance().setMySide(player.getSide());////进入战斗赋值 side信息
                //Stores.getInstance().setBasicInfo(player.getUserinfo());//我的用户信息 登录时已经赋值
                Stores.getInstance().setMyFightPlayerData(player);//我的战斗信息

                //方便召唤神话
                List<Wzdj.FightFighterData> fight5List = player.getFighters().stream()
                        .filter(actor -> actor.getFighterid().startsWith("f_5")).sorted((o1, o2) -> o2.getLv() - o1.getLv()).collect(Collectors.toList());
                Stores.getInstance().setMyFight5List(fight5List);//进入时

                WZDJMainFrame.myTextArea.setText(WZDJService.info(player));
            } else {//对方
                Stores.getInstance().setItUid(player.getUserinfo().getUid());//进入战斗赋值 对方uid
                Stores.getInstance().setItSide(player.getSide());//对方Side
                Stores.getInstance().setItBasicInfo(player.getUserinfo());//对方用户信息
                Stores.getInstance().setItFightPlayerData(player);//对方战斗信息

                List<Wzdj.FightFighterData> fight5List = player.getFighters().stream()
                        .filter(actor -> actor.getFighterid().startsWith("f_5")).sorted((o1, o2) -> o2.getLv() - o1.getLv()).collect(Collectors.toList());
                Stores.getInstance().setItFight5List(fight5List);
                WZDJMainFrame.otherTextArea.setText(WZDJService.info(player));//展示信息
            }
        }
    }


    //战斗同步资源,更新界面
    public static void syncResource(Channel channel, Wzdj.RpcClientFightSyncResource rpcClientFightSyncResource) {
        Integer side = rpcClientFightSyncResource.getSide() != null ? rpcClientFightSyncResource.getSide() : 0;
        String resourceid = rpcClientFightSyncResource.getResourceid();
        Integer after = rpcClientFightSyncResource.getAfter() != null ? rpcClientFightSyncResource.getAfter() : 0;
        Integer delta = rpcClientFightSyncResource.getDelta() != null ? rpcClientFightSyncResource.getDelta() : 0;
        Integer reason = rpcClientFightSyncResource.getReason() != null ? rpcClientFightSyncResource.getReason() : 0;

        String name = Consts.Resource.nameById(resourceid);
//        System.out.printf("%s,chg:%s,after:%s%n", name, delta, after);
        if (Stores.getInstance().getMySide() == side) {//自己的数据
            int wave = Stores.getInstance().getWave();
            if (Consts.Resource.MONSTER_LIMIT.getId().equals(resourceid)) {
                Stores.getInstance().setMonsterLimit(after);
            } else if (Consts.Resource.POPU_LIMIT.getId().equals(resourceid)) {
                Stores.getInstance().setMyPopuLimit(after);
            } else if (Consts.Resource.FOOD.getId().equals(resourceid)) {
                Stores.getInstance().setMyFood(after);
//                WZDJMainFrame.myFoodLabel.setText("食物:" + after);
            } else if (Consts.Resource.LUCK_COIN.getId().equals(resourceid)) {
                Stores.getInstance().setMyLuckCoin(after);
//                WZDJMainFrame.myLuckCoinLabel.setText("幸运:" + after);
            } else if (Consts.Resource.MONSTER.getId().equals(resourceid)) {
                Stores.getInstance().setMonsterNum(after);
//                WZDJMainFrame.monsterLabel.setText("怪:" + after + "/" + Stores.getInstance().getMonsterLimit());
            } else if (Consts.Resource.POPU.getId().equals(resourceid)) {
                Stores.getInstance().setMyPopuNum(after);
//                WZDJMainFrame.myPopuLabel.setText("英雄:" + after + "/" + Stores.getInstance().getMyPopuLimit());
            }
        } else {//对方的数据
            if (Consts.Resource.POPU_LIMIT.getId().equals(resourceid)) {
                Stores.getInstance().setItPopuLimit(after);
            } else if (Consts.Resource.FOOD.getId().equals(resourceid)) {
                Stores.getInstance().setItFood(after);
//                WZDJMainFrame.itFoodLabel.setText("it食物:" + after);
            } else if (Consts.Resource.LUCK_COIN.getId().equals(resourceid)) {
                Stores.getInstance().setItLuckCoin(after);
//                WZDJMainFrame.itLuckCoinLabel.setText("it幸运:" + after);
            } else if (Consts.Resource.POPU.getId().equals(resourceid)) {
                Stores.getInstance().setItPopuNum(after);
//                WZDJMainFrame.itPopuLabel.setText("it英雄:" + after + "/" + Stores.getInstance().getItPopuLimit());
            }
        }
    }


    public static void startWave(Channel channel, int wave, int waveTimeSecond) {
        Stores.getInstance().setWave(wave);
        Stores.getInstance().getWaveTime().set(waveTimeSecond);
        WZDJMainFrame.waveLabel.setText("回合:" + wave);//新回合开始
        //自动聊天
        if (Stores.getInstance().isFightWaveChat()) {
            waveChatMsg(wave, waveTimeSecond);//局内骚扰信息
        }
        //自动召唤
        if (Stores.getInstance().isAutoCall()) {
            if (wave >= 3 && wave <= 5) {
                for (int i = 0; i < 5; i++) {
//                        System.out.printf("回合:%s,i:%s,isFull:%s%n", wave, i, Stores.getInstance().isFightFull());
                    if (!Stores.getInstance().isFightFull()) {
                        WZDJSender.rpcServerFightFighterCall();// 关卡 3-5 每次回合开始召唤5个
//                            ThreadUtil.sleep(150);
                    }
                }
            } else if (wave % 5 == 0) {//
                int num = 1;//手动时可以自己操作召唤
                if (Stores.getInstance().getModel().get() == 1) {
                    num = 3;//自动时全靠自动化
                }
                for (int i = 0; i < num; i++) {
                    if (!Stores.getInstance().isFightFull()) {
                        WZDJSender.rpcServerFightFighterCall();//每5回合召唤3个
                    }
                }
            } else if (wave >= 30) {
                checkWujiAndStrengthen();
            }
        }
        //自动强化
        if (Stores.getInstance().isAutoStrengthen()) {
            if ((wave == 10 || wave == 15)) {
                for (int i = 0; i < 6; i++) {
                    WZDJSender.rpcServerFightEnhance(4);//强化召唤概率
                }
            } else if (wave >= 50) {//一关强化一次可以的
                WZDJSender.rpcServerFightEnhance(3);//强化金色和神话伤害
            }
        }
    }


    //获取用户基本信息
    public static String getUBase(Wzdj.BasicInfo basicInfo) {
        int vip = WZDJService.getVip(basicInfo);
        return String.format("%s|%s|%s|%s|", basicInfo.getUid(), basicInfo.getName(), vip, basicInfo.getLv());
    }

    //数据转换
    public static String info(Wzdj.FightPlayerData fightPlayerData) {
        StringBuilder info = new StringBuilder();
//        info.append("用户信息---------------------------------------------\n");
        Wzdj.BasicInfo basicInfo = fightPlayerData.getUserinfo();
        int vip = WZDJService.getVip(basicInfo);
        String uInfo = String.format("%s_%s[%s](%s)", basicInfo.getUid(), basicInfo.getName(), vip, basicInfo.getLv());
        info.append(uInfo).append("\n");
        info.append("Side:").append(fightPlayerData.getSide()).append("\n");
        info.append("战斗机---------\n");
        //根据等级倒叙排列，大的放上面
        List<Wzdj.FightFighterData> fight5List = fightPlayerData.getFighters().stream()
                .filter(actor -> actor.getFighterid().startsWith("f_5")).sorted((o1, o2) -> o2.getLv() - o1.getLv()).collect(Collectors.toList());
        //战斗机信息
        for (Wzdj.FightFighterData fighterData : fight5List) {
            String name = Consts.Fighter.nameById(fighterData.getFighterid());
            boolean trial = fighterData.getTrial() != null && fighterData.getTrial();
            info.append(name).append("=").append(fighterData.getLv()).append("级");
            if (trial) {
                info.append("(试用)");
            }
            info.append("\n");
        }
//        info.append("召唤---------------------------------------------\n");
//        fightPlayerData.getCallrates().forEach((k, v) -> {
//            String name = Consts.CallRate.nameById(k);
//            info.append(k).append("=").append(v).append("\n");
//        });
//        info.append("赌博---------------------------------------------\n");
//        fightPlayerData.getBetrates().forEach((k, v) -> {
//            String name = Consts.BetRate.nameById(k);
//            info.append(k).append("=").append(v).append("\n");
//        });
//        info.append("强化---------------------------------------------\n");
//        fightPlayerData.getEnhancelv().forEach((k, v) -> {
//            String name = Consts.Enhance.nameById(k);
//            info.append(k).append("=").append(v).append("级").append("\n");
//        });
        return info.toString();
    }

    //获取vip等级
    public static int getVip(Wzdj.BasicInfo basicInfo) {
        if (basicInfo.getFrame() != null) {
            try {
                return Integer.parseInt(basicInfo.getFrame().split("_")[1]);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return 0;
    }

    //快照信息处理
    public static void snapshot(Channel channel, Wzdj.SnapshotData snapshot) {
        //快照获取 side 和 用户信息
        for (Wzdj.FightPlayerData player : snapshot.getPlayers()) {
            if (Objects.equals(player.getUserinfo().getUid(), Stores.getInstance().getBasicInfo().getUid())) {
                Stores.getInstance().setMyUid(player.getUserinfo().getUid());//快照回复 uid信息
                Stores.getInstance().setMySide(player.getSide());//快照还原side信息
                Stores.getInstance().setMyFightPlayerData(player);//快照还原我的战斗信息
//                MemoryUtil.setBasicInfo(player.getUserinfo());//登录上来就有了
//                WZDJMainFrame.gridPane.setText("side:" + player.getSide());

                //方便召唤神话
                List<Wzdj.FightFighterData> fight5List = player.getFighters().stream()
                        .filter(actor -> actor.getFighterid().startsWith("f_5")).sorted((o1, o2) -> o2.getLv() - o1.getLv()).collect(Collectors.toList());
                Stores.getInstance().setMyFight5List(fight5List);//快照还原我的5级战斗机

                //设置英雄上限，和怪物上限
                player.getResources().forEach((k, v) -> {
                    if (Consts.Resource.MONSTER_LIMIT.getId().equals(k)) {
                        Stores.getInstance().setMonsterLimit(v);
                    } else if (Consts.Resource.POPU_LIMIT.getId().equals(k)) {
                        Stores.getInstance().setMyPopuLimit(v);
                    }
                });

                //恢复强化等级
                Stores.getInstance().getEnhance1().set(player.getEnhancelv().getOrDefault(1, 0));
                Stores.getInstance().getEnhance2().set(player.getEnhancelv().getOrDefault(2, 0));
                Stores.getInstance().getEnhance3().set(player.getEnhancelv().getOrDefault(3, 0));
                Stores.getInstance().getEnhance4().set(player.getEnhancelv().getOrDefault(4, 0));

                WZDJMainFrame.myTextArea.setText(info(player));//快照展示自己战斗机
            } else {//对方
                Stores.getInstance().setItUid(player.getUserinfo().getUid());//快照还原用户id
                Stores.getInstance().setItBasicInfo(player.getUserinfo());//快照还原对方用户信息
                Stores.getInstance().setItSide(player.getSide());//快照还原对方Side
                Stores.getInstance().setItFightPlayerData(player);//快照还原对方战斗信息

                List<Wzdj.FightFighterData> fight5List = player.getFighters().stream()
                        .filter(actor -> actor.getFighterid().startsWith("f_5")).sorted((o1, o2) -> o2.getLv() - o1.getLv()).collect(Collectors.toList());
                Stores.getInstance().setItFight5List(fight5List);
                WZDJMainFrame.otherTextArea.setText(info(player));//快照展示对方战斗机

                //设置英雄上限，和怪物上限
                player.getResources().forEach((k, v) -> {
                    if (Consts.Resource.POPU_LIMIT.getId().equals(k)) {
                        Stores.getInstance().setItPopuLimit(v);
                    }
                });
            }
        }
        //快照获取关卡信息
        Wzdj.WaveData wavedata = snapshot.getWavedata();
        Integer time = wavedata.getTime();
        Integer wave = wavedata.getWave();
        if (time != null && wave != null) {
            Stores.getInstance().setWave(wave);
            WZDJMainFrame.waveLabel.setText("回合:" + wave);//快照恢复回合数
        }
        //快照获取怪物和英雄信息
        List<Wzdj.FightActorData> actors = snapshot.getActors();
        for (Wzdj.FightActorData actor : actors) {
            if (actor.getActorid().startsWith("f")) {//只关注 1英雄类型，2自己的英雄，3有效英雄
                if (actor.getSide() == Stores.getInstance().getMySide()) {
                    gridNullHandler(actor, true);//空格子处理
                    //英雄个数map处理
                    CopyOnWriteArrayList<Wzdj.FightActorData> list = Stores.getInstance().getMyActorMap().getOrDefault(actor.getActorid(), new CopyOnWriteArrayList<>());
                    list.add(actor);
                    Stores.getInstance().getMyActorMap().put(actor.getActorid(), list);
                    //格子个数map处理
                    CopyOnWriteArrayList<Wzdj.FightActorData> gridList = Stores.getInstance().getMyGridMap().getOrDefault(actor.getGrid(), new CopyOnWriteArrayList<>());
                    gridList.add(actor);
                    Stores.getInstance().getMyGridMap().put(actor.getGrid(), gridList);
                } else {
                    gridNullHandler(actor, false);//空格子处理
                    //英雄个数map处理
                    CopyOnWriteArrayList<Wzdj.FightActorData> list = Stores.getInstance().getItActorMap().getOrDefault(actor.getActorid(), new CopyOnWriteArrayList<>());
                    list.add(actor);
                    Stores.getInstance().getItActorMap().put(actor.getActorid(), list);
                    //格子个数map处理
                    CopyOnWriteArrayList<Wzdj.FightActorData> gridList = Stores.getInstance().getItGridMap().getOrDefault(actor.getGrid(), new CopyOnWriteArrayList<>());
                    gridList.add(actor);
                    Stores.getInstance().getItGridMap().put(actor.getGrid(), gridList);
                }
            }
        }

        WZDJMainFrame.consoleTextArea.append("恢复快照------------------------------------\n");
        StringBuilder gridMsg = new StringBuilder();
        Stores.getInstance().getMyGridMap().forEach((k, v) -> {
            List<String> names = v.stream().map(fightActorData -> Consts.Fighter.nameById(fightActorData.getActorid())).collect(Collectors.toList());
            gridMsg.append(k).append("=").append(JSON.toJSONString(names)).append(";");
        });
        WZDJMainFrame.consoleTextArea.append("格子:" + gridMsg.toString() + "\n");
        WZDJMainFrame.consoleTextArea.append("恢复快照结束------------------------------------\n");
    }

    //空格子处理
    private static synchronized void gridNullHandler(Wzdj.FightActorData actor, boolean isMy) {
        if (actor.getGrid() != null) {
            return;
        }
        Integer grid = null;
        for (int i = 0; i < 19; i++) {
            ConcurrentHashMap<Integer, CopyOnWriteArrayList<Wzdj.FightActorData>> gridMap =
                    isMy ? Stores.getInstance().getMyGridMap() : Stores.getInstance().getItGridMap();
            List<Wzdj.FightActorData> gridList = gridMap.getOrDefault(i, new CopyOnWriteArrayList<>());
            if (gridList.isEmpty()) {
                grid = i;
                if (isMy) {
                    log.info("我 找格时 找到空格子:null->" + grid);
                }
                break;
            } else if (gridList.size() < 3 && gridList.get(0).getActorid().equals(actor.getActorid())) {
                grid = i;
                if (isMy) {
                    log.info("我 找格时 找到相同格:null->" + grid);
                }
                break;
            }
        }
        actor.setGrid(grid);
    }

    //新英雄处理
    public static synchronized void newActors(Channel channel, List<Wzdj.FightActorData> actors) {
        for (Wzdj.FightActorData actor : actors) {
            if (actor.getActorid().startsWith("f")) {
                String name = Consts.Fighter.nameById(actor.getActorid());
                if (actor.getSide() == Stores.getInstance().getMySide()) {
                    Integer oldGrid = actor.getGrid();
                    gridNullHandler(actor, true);//空格子处理
                    //英雄个数map处理
                    CopyOnWriteArrayList<Wzdj.FightActorData> list = Stores.getInstance().getMyActorMap().getOrDefault(actor.getActorid(), new CopyOnWriteArrayList<>());
                    list.add(actor);
                    Stores.getInstance().getMyActorMap().put(actor.getActorid(), list);
                    //格子个数map处理
                    CopyOnWriteArrayList<Wzdj.FightActorData> gridList = Stores.getInstance().getMyGridMap().getOrDefault(actor.getGrid(), new CopyOnWriteArrayList<>());
                    gridList.add(actor);
                    Stores.getInstance().getMyGridMap().put(actor.getGrid(), gridList);
//                    if (gridList.size() >= 4) {
//                        String format = String.format("%s英雄 官方Grid:%s,", name, oldGrid);
//                        log.error(format);
//                        List<String> collect = gridList.stream().map(fightActorData -> Consts.Fighter.nameById(fightActorData.getActorid())).collect(Collectors.toList());
//                        String newActorMsg = String.format("%s,%s格有%s个:%s,共有:%s个", name, actor.getGrid(), gridList.size(), JSON.toJSONString(collect), list.size());
//                        log.error(newActorMsg);
//                    }
                } else {
                    gridNullHandler(actor, false);//空格子处理
                    //英雄个数map处理
                    CopyOnWriteArrayList<Wzdj.FightActorData> list = Stores.getInstance().getItActorMap().getOrDefault(actor.getActorid(), new CopyOnWriteArrayList<>());
                    list.add(actor);
                    Stores.getInstance().getItActorMap().put(actor.getActorid(), list);
                    //格子个数map处理
                    CopyOnWriteArrayList<Wzdj.FightActorData> gridList = Stores.getInstance().getItGridMap().getOrDefault(actor.getGrid(), new CopyOnWriteArrayList<>());
                    gridList.add(actor);
                    Stores.getInstance().getItGridMap().put(actor.getGrid(), gridList);
                }
            }
        }
    }

    //删除英雄处理
    public static synchronized void removeActors(Channel channel, List<Wzdj.ActorRemoveData> removeactors) {
        for (Wzdj.ActorRemoveData actorRemoveData : removeactors) {
//            log.info("移除actorId:" + actorRemoveData.getId());
            //先从自己这里面找
            for (Map.Entry<String, CopyOnWriteArrayList<Wzdj.FightActorData>> entry : Stores.getInstance().getMyActorMap().entrySet()) {
                String actorId = entry.getKey();
                String name = Consts.Fighter.nameById(actorId);
                CopyOnWriteArrayList<Wzdj.FightActorData> fightActorDataList = entry.getValue();
                int oldSize = fightActorDataList.size();
                try {
                    fightActorDataList.removeIf(fightActorData -> fightActorData.getId().equals(actorRemoveData.getId()));
                } catch (Exception e) {
                    e.printStackTrace();
                }
                int chg = fightActorDataList.size() - oldSize;
                if (Math.abs(chg) > 0) {
//                    log.info("自己actorMap移除一个:" + name);
//                    WZDJMainFrame.consoleTextArea.append("自己actorMap移除一个:" + name + "\n");
                }
                Stores.getInstance().getMyActorMap().put(name, fightActorDataList);//移除掉
            }
            for (Map.Entry<Integer, CopyOnWriteArrayList<Wzdj.FightActorData>> entry : Stores.getInstance().getMyGridMap().entrySet()) {
                Integer grid = entry.getKey();
                CopyOnWriteArrayList<Wzdj.FightActorData> fighters = entry.getValue();
                int oldSize = fighters.size();
                try {
                    fighters.removeIf(fighter -> fighter.getId().equals(actorRemoveData.getId()));
                } catch (Exception e) {
                    e.printStackTrace();
                }
                int chg = fighters.size() - oldSize;
                if (Math.abs(chg) > 0) {
//                    log.info("自己gridMap第" + grid + "格移除一个");
//                    WZDJMainFrame.consoleTextArea.append("自己gridMap第" + grid + "格移除一个" + "\n");
                }
                Stores.getInstance().getMyGridMap().put(grid, fighters);//移除掉
//                WZDJMainFrame.playPanel.update(Stores.getInstance().getMyGridMap(), true);//自己移除更新
            }

            //再从对面格子里找
            for (Map.Entry<String, CopyOnWriteArrayList<Wzdj.FightActorData>> entry : Stores.getInstance().getItActorMap().entrySet()) {
                String actorId = entry.getKey();
                String name = Consts.Fighter.nameById(actorId);
                CopyOnWriteArrayList<Wzdj.FightActorData> fightActorDataList = entry.getValue();
                int oldSize = fightActorDataList.size();
                try {
                    fightActorDataList.removeIf(fightActorData -> fightActorData.getId().equals(actorRemoveData.getId()));
                } catch (Exception e) {
                    e.printStackTrace();
                }
                int chg = fightActorDataList.size() - oldSize;
                if (Math.abs(chg) > 0) {
//                    log.info("对面actorMap移除一个:" + name);
//                    WZDJMainFrame.consoleTextArea.append("对面actorMap移除一个:" + name + "\n");
                }
                Stores.getInstance().getItActorMap().put(name, fightActorDataList);//移除掉
            }
            for (Map.Entry<Integer, CopyOnWriteArrayList<Wzdj.FightActorData>> entry : Stores.getInstance().getItGridMap().entrySet()) {
                Integer grid = entry.getKey();
                CopyOnWriteArrayList<Wzdj.FightActorData> fighters = entry.getValue();
                int oldSize = fighters.size();
                try {
                    fighters.removeIf(fighter -> fighter.getId().equals(actorRemoveData.getId()));
                } catch (Exception e) {
                    e.printStackTrace();
                }
                int chg = fighters.size() - oldSize;
                if (Math.abs(chg) > 0) {
//                    log.info("对面gridMap第" + grid + "格移除一个");
//                    WZDJMainFrame.consoleTextArea.append("对面gridMap第" + grid + "格移除一个" + "\n");
                }
                Stores.getInstance().getItGridMap().put(grid, fighters);//移除掉
//                WZDJMainFrame.playPanel.update(Stores.getInstance().getItGridMap(), false);//对方移除更新
            }
        }
    }


    //按钮点击事件----------------------------------------------------------------------------------------------------
    //控制台打印英雄信息

    //召唤神话
    public static void checkMythic() {
        if (Stores.getInstance().getMyActorMap().size() < 3) {//未满3不处理
            return;
        }
        for (Consts.Fighter fighter : Consts.Fighter.values()) {
            Stores.getInstance().getMyActorMap().putIfAbsent(fighter.getId(), new CopyOnWriteArrayList<>());
        }
//        if (Stores.getInstance().getMyActorMap().get(Consts.Fighter.F3004.getId()).size() > 0
//                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F3001.getId()).size() > 0
//                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F1005.getId()).size() > 0) {
//            CopyOnWriteArrayList<Wzdj.FightActorData> list = Stores.getInstance().getMyActorMap().getOrDefault(Consts.Fighter.F5001.getId(), new CopyOnWriteArrayList<>());
//            if (Stores.getInstance().getWave() < 30 && list.size() < 1) {
//                WZDJSender.rpcServerFightFighterSyntheticMythic(Consts.Fighter.F5001.getId());//青蛙
//            }
//        }
        if (Stores.getInstance().getMyActorMap().get(Consts.Fighter.F3005.getId()).size() > 0
                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F3003.getId()).size() > 0
                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F2001.getId()).size() > 0) {
            CopyOnWriteArrayList<Wzdj.FightActorData> list = Stores.getInstance().getMyActorMap().getOrDefault(Consts.Fighter.F5002.getId(), new CopyOnWriteArrayList<>());
            if (list.size() <= 5) {
                WZDJSender.rpcServerFightFighterSyntheticMythic(Consts.Fighter.F5002.getId());//财神
            }
        }

        if (Stores.getInstance().getMyActorMap().get(Consts.Fighter.F3002.getId()).size() > 0
                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F3004.getId()).size() > 0
                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F2003.getId()).size() > 0) {
            CopyOnWriteArrayList<Wzdj.FightActorData> list = Stores.getInstance().getMyActorMap().getOrDefault(Consts.Fighter.F5027.getId(), new CopyOnWriteArrayList<>());
            if (list.size() <= 4) {
                WZDJSender.rpcServerFightFighterSyntheticMythic(Consts.Fighter.F5027.getId());//卡皮巴拉
            }
        }
        //合成表 f_3005,f_2004,f_2001
//        if (Stores.getInstance().getMyActorMap().get(Consts.Fighter.F3005.getId()).size() > 0
//                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F2004.getId()).size() > 0
//                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F2001.getId()).size() > 0) {
//            CopyOnWriteArrayList<Wzdj.FightActorData> list = Stores.getInstance().getMyActorMap().getOrDefault(Consts.Fighter.F5003.getId(), new CopyOnWriteArrayList<>());
//            if (Stores.getInstance().getBasicInfo().getLv() < 8 && list.size() < 3) {//等级小于10才用剑豪
//                WZDJSender.rpcServerFightFighterSyntheticMythic(Consts.Fighter.F5003.getId());//剑豪
//            }
//        }
        if (Stores.getInstance().getMyActorMap().get(Consts.Fighter.F4003.getId()).size() > 0
                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F3004.getId()).size() > 0
                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F2004.getId()).size() > 0) {
            CopyOnWriteArrayList<Wzdj.FightActorData> list = Stores.getInstance().getMyActorMap().getOrDefault(Consts.Fighter.F5004.getId(), new CopyOnWriteArrayList<>());
            if (list.size() <= 4) {
                WZDJSender.rpcServerFightFighterSyntheticMythic(Consts.Fighter.F5004.getId());//无极大师
            }
        }
        if (Stores.getInstance().getMyActorMap().get(Consts.Fighter.F3001.getId()).size() > 0
                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F1001.getId()).size() > 0
                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F1004.getId()).size() > 1) {
            CopyOnWriteArrayList<Wzdj.FightActorData> list = Stores.getInstance().getMyActorMap().getOrDefault(Consts.Fighter.F5008.getId(), new CopyOnWriteArrayList<>());
            if (list.size() <= 6) {
                WZDJSender.rpcServerFightFighterSyntheticMythic(Consts.Fighter.F5008.getId());//喵利波特
            }
        }
        if (Stores.getInstance().getMyActorMap().get(Consts.Fighter.F4001.getId()).size() > 0
                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F3005.getId()).size() > 0
                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F1003.getId()).size() > 0) {
            CopyOnWriteArrayList<Wzdj.FightActorData> list = Stores.getInstance().getMyActorMap().getOrDefault(Consts.Fighter.F5009.getId(), new CopyOnWriteArrayList<>());
            if (list.size() <= 4) {
                WZDJSender.rpcServerFightFighterSyntheticMythic(Consts.Fighter.F5009.getId());//巨人
            }
        }
        if (Stores.getInstance().getMyActorMap().get(Consts.Fighter.F4001.getId()).size() > 0
                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F3003.getId()).size() > 0
                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F1002.getId()).size() > 0
                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F1003.getId()).size() > 0) {//悟空
            CopyOnWriteArrayList<Wzdj.FightActorData> list = Stores.getInstance().getMyActorMap().getOrDefault(Consts.Fighter.F5015.getId(), new CopyOnWriteArrayList<>());
            if (list.size() <= 4) {
                WZDJSender.rpcServerFightFighterSyntheticMythic(Consts.Fighter.F5015.getId());//巨人
            }
        }
//        if (Stores.getInstance().getMyActorMap().get(Consts.Fighter.F4002.getId()).size() > 0
//                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F2005.getId()).size() > 0
//                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F1004.getId()).size() > 0) {
//            CopyOnWriteArrayList<Wzdj.FightActorData> list = Stores.getInstance().getMyActorMap().getOrDefault(Consts.Fighter.F5010.getId(), new CopyOnWriteArrayList<>());
//            if (list.size() < 1) {
//                WZDJSender.rpcServerFightFighterSyntheticMythic(Consts.Fighter.F5010.getId());//冰女
//            }
//        }
//        if (Stores.getInstance().getMyActorMap().get(Consts.Fighter.F3001.getId()).size() > 1
//                && Stores.getInstance().getMyActorMap().get(Consts.Fighter.F1004.getId()).size() > 0) {
//            WZDJSender.rpcServerFightFighterSyntheticMythic(Consts.Fighter.F5012.getId());//龙
//        }
    }

    //合并3个的
    public static void checkCombination() {
        List<String> keepList = new CopyOnWriteArrayList<>();
        keepList.add(Consts.Fighter.F1005.getId());//兽人
        keepList.add(Consts.Fighter.F2001.getId());//小恶魔
        for (Map.Entry<Integer, CopyOnWriteArrayList<Wzdj.FightActorData>> entry : Stores.getInstance().getMyGridMap().entrySet()) {
            Integer grid = entry.getKey();
            CopyOnWriteArrayList<Wzdj.FightActorData> gridList = entry.getValue();
            if (gridList.isEmpty()) {
                continue;
            }
            Wzdj.FightActorData actor = gridList.get(0);
            String actorid = actor.getActorid();
            String name = Consts.Fighter.nameById(actor.getActorid());
            List<Wzdj.FightActorData> list = Stores.getInstance().getMyActorMap().getOrDefault(actor.getActorid(), new CopyOnWriteArrayList<>());
            if (gridList.size() >= 3) {
                if (actorid.startsWith("f_1") || actorid.startsWith("f_2") || actorid.startsWith("f_3")) {
                    if (list.size() >= 6) {//同类型已经有6个了直接合并一个
                        System.out.printf("%s太多已有%s个,合并格%s,%n", name, list.size(), grid);
                        WZDJSender.rpcServerFightFighterSynthetic(grid);//满6个自动合成
                    } else if (Stores.getInstance().getBasicInfo().getLv() > 12 && Stores.getInstance().getWave() < 45 && keepList.contains(actor.getActorid())) {
                        //45关之前 兽人和小恶魔不合并
//                        System.out.printf("关卡%s,%s有%s个,不处理格:%s,%n", Stores.getInstance().getWave(), name, list.size(), grid);
                    } else if (Stores.keepFighter.contains(actor.getActorid()) || Consts.Fighter.F3002.getId().equals(actor.getActorid())) {
                        //需要的不合并
//                        System.out.printf("关卡%s,%s有%s个,不处理格:%s,%n", Stores.getInstance().getWave(), name, list.size(), grid);
                    } else {
                        //其他的都合并
                        WZDJSender.rpcServerFightFighterSynthetic(grid);//合成
                    }
                } else if (actorid.startsWith("f_4")) {//金色超过3个要卖掉
                    WZDJService.remove(actor);//金色超过3个要卖掉
                }
            }
        }

        if (Stores.getInstance().isFightFull()) {
            for (int i = 17; i > 0; i--) {
                CopyOnWriteArrayList<Wzdj.FightActorData> actorList = Stores.getInstance().getMyGridMap().getOrDefault(i, new CopyOnWriteArrayList<>());
                if (actorList.isEmpty()) {
                    continue;
                }
                Wzdj.FightActorData actor = actorList.get(0);
                String actorId = actor.getActorid();
                String name = Consts.Fighter.nameById(actorId);
                if (actorList.size() == 3 && (actorId.startsWith("f_1") || actorId.startsWith("f_2") || actorId.startsWith("f_3"))) {
                    log.info("满员了,没法了,合并单元格:" + i + ",name:" + name);
                    WZDJSender.rpcServerFightFighterSynthetic(i);
                }
            }
        }


    }


    //检查移动
    public static void checkMove() {
        //move
        for (Map.Entry<Integer, CopyOnWriteArrayList<Wzdj.FightActorData>> entity : Stores.getInstance().getMyGridMap().entrySet()) {
            Integer grid = entity.getKey();
            CopyOnWriteArrayList<Wzdj.FightActorData> fighters = entity.getValue();
            if (fighters.isEmpty()) {
                continue;
            }
            Wzdj.FightActorData actorData = fighters.get(0);
            String actorid = actorData.getActorid();
            String name = Consts.Fighter.nameById(actorid);
            if (actorid.equals(Consts.Fighter.F1005.getId())) {
                int[] indexArr = new int[]{1, 2, 0};//兽人站位
                List<Integer> arr = Arrays.stream(indexArr).boxed().collect(Collectors.toList());
                if (arr.contains(grid)) {
                    continue;
                }
                for (int i : indexArr) {
                    List<Wzdj.FightActorData> grIdlist = Stores.getInstance().getMyGridMap().getOrDefault(i, new CopyOnWriteArrayList<>());
                    if (!grIdlist.isEmpty() && (grIdlist.get(0).getActorid().startsWith("f_5")//已有高级英雄
                            || grIdlist.get(0).getActorid().equals(Consts.Fighter.F1005.getId()))) {//已有兽人
                        continue;
                    } else {//移动到指定位置
                        if (grid != i) {
                            log.info("兽人 移动 move:" + grid + ",to:" + i);
                            move(grid, i);
                            break;
                        }
                    }
                }
            } else if (actorid.equals(Consts.Fighter.F3002.getId())) {//电磁坦克站位
                List<Wzdj.FightActorData> gridAfterList = Stores.getInstance().getMyGridMap().getOrDefault(3, new CopyOnWriteArrayList<>());
                if (!gridAfterList.isEmpty() && gridAfterList.get(0).getActorid().equals(Consts.Fighter.F1005.getId())) {
                    //已经是电磁坦克就不动,不是就动
                    continue;
                } else {
                    if (grid != 3) {
                        move(grid, 3);
                    }
                }
            } else if (actorid.equals(Consts.Fighter.F5002.getId())) {
                int[] indexArr = new int[]{0, 1, 2, 6, 9, 12, 15, 16, 17, 14, 11, 8, 5};//财神站位
                List<Integer> arr = Arrays.stream(indexArr).boxed().collect(Collectors.toList());
                if (arr.contains(grid)) {
                    continue;
                }
                moveMythicGrid(grid, indexArr);
            } else if (actorid.equals(Consts.Fighter.F5027.getId())) {
                int[] indexArr = new int[]{13, 14, 10, 11};//卡皮巴拉
                List<Integer> arr = Arrays.stream(indexArr).boxed().collect(Collectors.toList());
                if (arr.contains(grid)) {
                    continue;
                }
                moveMythicGrid(grid, indexArr);
            } else if (actorid.equals(Consts.Fighter.F5003.getId())) {
                int[] indexArr = new int[]{10, 12, 13};//剑豪站位
                List<Integer> arr = Arrays.stream(indexArr).boxed().collect(Collectors.toList());
                if (arr.contains(grid)) {
                    continue;
                }
                moveMythicGrid(grid, indexArr);
            } else if (actorid.equals(Consts.Fighter.F5008.getId())) {//喵利波特站位
                int[] indexArr = new int[]{4, 5, 7, 8, 10, 11};
                List<Integer> arr = Arrays.stream(indexArr).boxed().collect(Collectors.toList());
                if (arr.contains(grid)) {
                    continue;
                }
                moveMythicGrid(grid, indexArr);
            } else if (actorid.equals(Consts.Fighter.F5001.getId())) {//青蛙
                int[] indexArr = new int[]{0, 6, 9, 12, 15};
                List<Integer> arr = Arrays.stream(indexArr).boxed().collect(Collectors.toList());
                if (arr.contains(grid)) {
                    continue;
                }
                moveMythicGrid(grid, indexArr);
            } else if (
                    actorid.equals(Consts.Fighter.F5009.getId()) //巨人
                    || actorid.equals(Consts.Fighter.F5004.getId())//无极大师
                    || actorid.equals(Consts.Fighter.F5015.getId())//孙悟空
            ) {
                int[] indexArr = new int[]{0, 6, 9, 12, 15};//无极大师和巨人站位
                List<Integer> arr = Arrays.stream(indexArr).boxed().collect(Collectors.toList());
                if (arr.contains(grid)) {
                    continue;
                }
                moveMythicGrid(grid, indexArr);
            } else if (actorid.equals(Consts.Fighter.F5010.getId()) || actorid.equals(Consts.Fighter.F5012.getId())) {
                int[] indexArr = new int[]{13, 14, 16, 17};//冰女和龙站位
                List<Integer> arr = Arrays.stream(indexArr).boxed().collect(Collectors.toList());
                if (arr.contains(grid)) {
                    continue;
                }
                moveMythicGrid(grid, indexArr);
            } else if (actorid.startsWith("f_5") || actorid.startsWith("f_4")) {//金色紫色英雄靠边站位
                if (Consts.nearList.contains(actorid)) {//近距离移动
                    int[] indexArr = new int[]{0, 6, 9, 12, 15, 16, 17, 14, 11};
                    List<Integer> arr = Arrays.stream(indexArr).boxed().collect(Collectors.toList());
                    if (arr.contains(grid)) {
                        continue;
                    }
                    log.info("近距离移动紫和金");
                    move34Grid(grid, indexArr);
                } else if (Consts.farList.contains(actorid)) {//远距离移动
                    int[] indexArr = new int[]{4, 7, 10, 13};
                    List<Integer> arr = Arrays.stream(indexArr).boxed().collect(Collectors.toList());
                    if (arr.contains(grid)) {
                        continue;
                    }
//                    log.info("远距离移动紫和金");
                    move34Grid(grid, indexArr);
                }

            }
        }
    }


    //发送移动
    public static void move(int from, int to) {
        int lastFromGrid = Stores.getInstance().getLastFromGrid();
        int lastToGrid = Stores.getInstance().getLastToGrid();
        if (lastFromGrid == from && lastToGrid == to) {//刚移动过就想移动回来，拿还是算了吧
            return;
        }
        if (lastFromGrid == to && lastToGrid == from) {
            return;
        }

        Stores.getInstance().setLastFromGrid(from);
        Stores.getInstance().setLastToGrid(to);

        WZDJSender.rpcServerFightFighterMove(from, to);
    }

    public static boolean remove(Wzdj.FightActorData fightActorData) {
        if (fightActorData.getId() == Stores.getInstance().getLastRemoveId()) {
            CopyOnWriteArrayList<Wzdj.FightActorData> list1 = Stores.getInstance().getMyGridMap().getOrDefault(fightActorData.getGrid(), new CopyOnWriteArrayList<>());
            list1.removeIf(actorData -> {
                return actorData.getId().equals(fightActorData.getId());
            });
            Stores.getInstance().getMyGridMap().put(fightActorData.getGrid(), list1);

            if (fightActorData.getActorid() != null) {
                CopyOnWriteArrayList<Wzdj.FightActorData> list2 = Stores.getInstance().getMyActorMap().getOrDefault(fightActorData.getActorid(), new CopyOnWriteArrayList<>());
                list2.removeIf(actorData -> actorData.getId().equals(fightActorData.getId()));
                Stores.getInstance().getMyActorMap().put(fightActorData.getActorid(), list2);
            }
            String name = Consts.Fighter.nameById(fightActorData.getActorid());
            System.err.printf("重复删除id为:%s的%s%n", fightActorData.getId(), name);
            return false;
        }
        Stores.getInstance().setLastRemoveId(fightActorData.getId());
        WZDJSender.rpcServerFightFighterRemove(fightActorData.getGrid(), fightActorData.getId());
        return true;
    }

    //移动神话英雄到指定位置
    private static void moveMythicGrid(int grid, int[] toArr) {
        for (int toGrid : toArr) {
            List<Wzdj.FightActorData> myGridList = Stores.getInstance().getMyGridMap().getOrDefault(grid, new CopyOnWriteArrayList<>());
            if (myGridList.isEmpty()) {
                log.error(String.format("发送错误,要移动的%s格,为啥没英雄", grid));
                break;
            }
            String myGridActorId = myGridList.get(0).getActorid();
            String myName = Consts.Fighter.nameById(myGridActorId);
            List<Wzdj.FightActorData> toGridlist = Stores.getInstance().getMyGridMap().getOrDefault(toGrid, new CopyOnWriteArrayList<>());
            if (toGridlist.isEmpty()) {
                log.info(String.format("移动:%s格(%s)->%s格(空)", grid, myName, toGrid));
                move(grid, toGrid);//神话向空格移动
                break;
            }
            String toActorId = toGridlist.get(0).getActorid();
            if (!isBig(toActorId) && grid != toGrid) {
                String toName = Consts.Fighter.nameById(toActorId);
                log.info(String.format("移动:%s格(%s)->%s格(%s)", grid, myName, toGrid, toName));
                move(grid, toGrid);//神话替换
                break;//一次检查只移动一个
            }
        }
    }

    public static boolean isBig(String actorId) {
        if (actorId.equals("f_9003")) {
            return false;
        }
        return actorId.startsWith("f_5") || actorId.startsWith("f_9");
    }


    private static void move34Grid(int grid, int[] indexArr) {
        for (int i : indexArr) {
            List<Wzdj.FightActorData> list = Stores.getInstance().getMyGridMap().getOrDefault(i, new CopyOnWriteArrayList<>());
            if (list.isEmpty()) {
                log.info(String.format("%s格为空,移动move:%s,to:%s", i, grid, i));
                move(grid, i);
                break;
            }
            String actorid = list.get(0).getActorid();
            boolean has = actorid.startsWith("f_3") || actorid.startsWith("f_4") || actorid.startsWith("f_5") || actorid.startsWith("f_9");
            if (has && !"f_9003".equals(actorid)) {//已有高级英雄,且不是小火苗
                continue;
            }
            String name = Consts.Fighter.nameById(actorid);
            if (grid != i) {
                log.info(String.format("%s格英雄[%s]没有我牛,我移动过去move:%s,to:%s", i, name, grid, i));
                move(grid, i);
                break;
            }
        }
    }

    //检测删除
    public static synchronized void checkRemove() {
        //满格子 删除逻辑
        if (Stores.getInstance().isFightFull()) {//满员一次一个
            for (int i = 17; i > 0; i--) {
                CopyOnWriteArrayList<Wzdj.FightActorData> actorList = Stores.getInstance().getMyGridMap().getOrDefault(i, new CopyOnWriteArrayList<>());
                if (actorList.isEmpty()) {
                    continue;
                }
                Wzdj.FightActorData actor = actorList.get(0);
                String actorId = actor.getActorid();
                String name = Consts.Fighter.nameById(actorId);
                if (Consts.Fighter.F9003.getId().equals(actorId) || actorId.startsWith("f_1") || actorId.startsWith("f_2")) {
                    if (!Stores.keepFighter.contains(actorId) && !actorId.startsWith("f_5")) {
                        if (actor.getId() == Stores.getInstance().getLastRemoveId()) {//跳过一次重复的
                            continue;
                        }
                        log.info(String.format("满员删除:%s格(%s),id:%s,上次删除id:%s",
                                i, name, actor.getId(), Stores.getInstance().getLastRemoveId()));
//                        WZDJService.remove(actor.getGrid(), actor.getId());//检查满格删除
                        actor.setGrid(i);
                        WZDJService.remove(actor);//检查满格删除
                        return;
                    }
                }
            }
        }

        //70关 删除2级换幸运币
        if (Stores.getInstance().getWave() > 70) {
            for (int i = 17; i > 0; i--) {
                CopyOnWriteArrayList<Wzdj.FightActorData> actorList = Stores.getInstance().getMyGridMap().getOrDefault(i, new CopyOnWriteArrayList<>());
                if (actorList.isEmpty()) {
                    continue;
                }
                Wzdj.FightActorData actor = actorList.get(0);
                String actorId = actor.getActorid();
                String name = Consts.Fighter.nameById(actorId);
                if (Consts.Fighter.F9003.getId().equals(actorId) || actorId.startsWith("f_2") && actor.getId() != Stores.getInstance().getLastRemoveId()) {
                    log.info(String.format("70关 删除2级换幸运币:%s格(%s)", i, name));
                    actor.setGrid(i);
                    WZDJService.remove(actor);//70关 自动删除2级换幸运币
                    return;
                }
            }
        }


        if (Stores.getInstance().getWave() > 45) {
            for (int i = 17; i > 0; i--) {
                CopyOnWriteArrayList<Wzdj.FightActorData> actorList = Stores.getInstance().getMyGridMap().getOrDefault(i, new CopyOnWriteArrayList<>());
                if (actorList.isEmpty()) {
                    continue;
                }
                Wzdj.FightActorData actor = actorList.get(0);
                String actorId = actor.getActorid();
                String name = Consts.Fighter.nameById(actorId);

                if (!Stores.keepFighter.contains(actorId) && !actorId.startsWith("f_5") && actor.getId() != Stores.getInstance().getLastRemoveId()) {
                    log.info(String.format("删除 不想要的:%s格(%s)", i, name));
                    actor.setGrid(i);
                    WZDJService.remove(actor);
                    return;
                }
            }
        }
    }

    //强化英雄 无极|悟空
    public static void checkWujiAndStrengthen() {
        Stores.getInstance().getMyGridMap().forEach((grid, list) -> {
            if (list.size() == 1) {
                Wzdj.FightActorData actor = list.get(0);
                if (actor.getActorid().equals(Consts.Fighter.F5004.getId()) || actor.getActorid().equals(Consts.Fighter.F5015.getId())) {//如果有无极,强化无极
                    WZDJSender.rpcServerFightFighterAttrsShow(actor.getId());//升级英雄属性
                    WZDJSender.rpcServerFightFighterOperate(actor.getId());//升级英雄属性
                }
            }
        });
    }


    public static void onChatMessage(Wzdj.RpcClientChatSend rpcClientChatSend) {
        try {
//            log.info(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS") + wzdjPack.toString());
            Wzdj.BasicInfo basicInfo = rpcClientChatSend.getResult().getSenderuser();
            int vip = getVip(basicInfo);
            Integer senderUid = basicInfo.getUid();//用户id 可以加好友用
            String vuInfo = String.format("%s_%s[%s](%s)", senderUid, basicInfo.getName(), vip, basicInfo.getLv());
            if (rpcClientChatSend.getChattype() == 1) {//大厅聊天消息
                WZDJMainFrame.homeChatArea.append(String.format("%s:%s%n", vuInfo, rpcClientChatSend.getResult().getContent()));
            } else if (rpcClientChatSend.getChattype() == 5) {//大厅 发布房间信息招募
//                String extra = rpcClientChatSend.getResult().getExtra();
//                JSONObject jsonObject = JSON.parseObject(extra);
//                int battleType = jsonObject.getIntValue("battleType");
//                String roomNum = jsonObject.getString("token");
//                if (battleType == 1) {//普通    如果对方等级高 可以加入战斗
//                    WZDJMainFrame.homeChatArea.append(String.format("%s:%s%n", vuInfo, "【普通】" + roomNum));
//                } else if (battleType == 2) {//困难  如果对方等级高 可以加入战斗
//                    WZDJMainFrame.homeChatArea.append(String.format("%s:%s%n", vuInfo, "【困难】" + roomNum));
//                }
            } else if (rpcClientChatSend.getChattype() == 6) {//战斗过程中，私聊消息发送    fixme 已走新协议了。
                String extra = rpcClientChatSend.getResult().getExtra();
                JSONObject jsonObject = JSON.parseObject(extra);
                String userId = jsonObject.getString("uid");//to用户id
                if (userId.equals(Stores.getInstance().getMyUid() + "")) {//是给我发送的消息，才恢复
                    String content = rpcClientChatSend.getResult().getContent();
                    //价值2亿的AI聊天机器人
                    content = content.replace("吗", "")
                            .replace("嘛", "")
                            .replace("？", "!")
                            .replace("?", "!")
                    ;
                }
                WZDJMainFrame.fightChatArea.append(String.format("%s:%s%n", vuInfo, rpcClientChatSend.getResult().getContent()));
            } else {
                log.error("不识别的聊天类型" + JSON.toJSONString(rpcClientChatSend));
            }
        } catch (Exception e) {
            log.error(JSON.toJSONString(rpcClientChatSend));
            e.printStackTrace();
        }
    }

    //局内聊天，单独拆出来一个协议
    public static void onFightChatMessage(Wzdj.RpcClientFightChatSend rpcClientFightChatSend) {
        log.info("局内聊天:" + JSON.toJSONString(rpcClientFightChatSend));
        Integer side = rpcClientFightChatSend.getSide() != null ? rpcClientFightChatSend.getSide() : -1;
        Wzdj.BasicInfo basicInfo = side == Stores.getInstance().getMySide() ? Stores.getInstance().getBasicInfo() : Stores.getInstance().getItBasicInfo();
        int vip = getVip(basicInfo);
        Integer senderUid = basicInfo.getUid();//用户id 可以加好友用

        String time = DateUtil.format(new Date(), "HH:mm:ss");
        String vuInfo = String.format("%s_%s[%s](%s)", senderUid, basicInfo.getName(), vip, basicInfo.getLv());
        String content = rpcClientFightChatSend.getContent();
        WZDJMainFrame.fightChatArea.append(String.format("%s %s:%s%n", time, vuInfo, content));

        String extra = rpcClientFightChatSend.getExtra();
        JSONObject jsonObject = JSON.parseObject(extra);
        String toUid = jsonObject.getString("uid");//to用户id

        int mySide = Stores.getInstance().getMySide();
        int itSide = Stores.getInstance().getItSide();
        int myUid = Stores.getInstance().getMyUid();
        int itUid = Stores.getInstance().getItUid();
        log.info(String.format("mySide:%s,itSide:%s,myUid:%s,itUid:%s", mySide, itSide, myUid, itUid));
        log.info(String.format("fromSide:%s,toUid:%s", side, toUid));
        if (Stores.getInstance().isFightReplayChat()) {//是否需要回复
            if (toUid.equals(Stores.getInstance().getMyUid() + "")) {//是给我发送的消息，才回答
                if (content.startsWith("emoji_")) {//表情
                    WZDJSender.rpcServerChatSend(content, 6);//局内聊天
                } else {
                    String replay = replaceAIChat(content);
                    String sendText = StrUtil.subWithLength(replay, 0, 100);
                    if (lastSend.equals(sendText) && !StrUtil.isBlank(lastSend) && Stores.getInstance().isFightReplayChat()) {//防止两个机器人互相循环
                        return;
                    }
                    lastSend = sendText;
                    WZDJSender.rpcServerChatSend(sendText, 6);//局内聊天
                }
            }
        }
    }

    public static String lastSend = "";

    public static String replaceAIChat(String content) {
        //String prefix = "";
        String prefix = "wx[dahuo_yzs]:";
        content = content.replace(prefix, "");//万一对方发的消息里有前缀，测提前删除前缀，避免成环的时候无限加长度
        String replay = prefix + content.replace("吗", "")//价值2亿的AI聊天机器人
                .replace("嘛", "")
                .replace("？", "!")
                .replace("?", "!");
        replay = StrUtil.appendIfMissing(replay, "!");
        return replay;
    }


    public static void signInAll(Channel channel, Wzdj.RpcClientSignInAll rpcClientSignInAll) {
        Integer resigncnt = rpcClientSignInAll.getResigncnt() != null ? rpcClientSignInAll.getResigncnt() : 0;
        Integer actid = rpcClientSignInAll.getActid();
        List<Wzdj.SignInData> signindatas = rpcClientSignInAll.getSignindatas() != null ? rpcClientSignInAll.getSignindatas() : new ArrayList<>();
        List<Wzdj.TotalSignInRewardData> totalsigninrewarddatas = rpcClientSignInAll.getTotalsigninrewarddatas() != null ? rpcClientSignInAll.getTotalsigninrewarddatas() : new ArrayList<>();
        StringBuilder sb = new StringBuilder();
        sb.append("签到基本信息_").append(String.format("actId:%s,resigncnt:%s", actid, resigncnt)).append("\n");
        sb.append("签到每日信息:").append(signindatas.size()).append("天,信息如下").append("\n");
        int signCnt = 0;
        for (Wzdj.SignInData signindata : signindatas) {
            Integer day = signindata.getDay();
            int status = signindata.getStatus() != null ? signindata.getStatus() : 0;
            if (status == 3) {
                signCnt++;
            }
            String statusName = Consts.SignState.nameById(status);
            sb.append(String.format("%s=%s(%s);", day, status, statusName));
        }
        int size = totalsigninrewarddatas.size();
        sb.append("\n签到上方累计 ").append(String.format("%s个累计进度,本月累计签到:%s次,状态如下", size, signCnt)).append("\n");
        if (size == 4) {
            Integer state1 = totalsigninrewarddatas.get(0).getRewardstatus();//5
            Integer state2 = totalsigninrewarddatas.get(1).getRewardstatus();//10
            Integer state3 = totalsigninrewarddatas.get(2).getRewardstatus();//16
            Integer state4 = totalsigninrewarddatas.get(3).getRewardstatus();//25
            int status1 = state1 != null ? state1 : -1;
            int status2 = state2 != null ? state2 : -1;
            int status3 = state3 != null ? state3 : -1;
            int status4 = state4 != null ? state4 : -1;
            if (status1 == -1 && signCnt >= 5) {
                status1 = 2;
            }
            if (status2 == -1 && signCnt >= 10) {
                status2 = 2;
            }
            if (status3 == -1 && signCnt >= 16) {
                status3 = 2;
            }
            if (status4 == -1 && signCnt >= 25) {
                status4 = 2;
            }
            sb.append(String.format("5=%s(%s);", status1, Consts.SignTotalState.nameById(status1)));
            sb.append(String.format("10=%s(%s);", status2, Consts.SignTotalState.nameById(status2)));
            sb.append(String.format("16=%s(%s);", status3, Consts.SignTotalState.nameById(status3)));
            sb.append(String.format("25=%s(%s);", status4, Consts.SignTotalState.nameById(status4)));
            //签到累计奖励 领取
            if (status1 == 2) {
                WZDJMainFrame.consoleTextArea.append("领取5天累计签到奖励\n");
                WZDJSender.rpcServerTotalSignInReward((1));
            }
            if (status2 == 2) {
                WZDJMainFrame.consoleTextArea.append("领取10天累计签到奖励\n");
                WZDJSender.rpcServerTotalSignInReward((2));
            }
            if (status3 == 2) {
                WZDJMainFrame.consoleTextArea.append("领取16天累计签到奖励\n");
                WZDJSender.rpcServerTotalSignInReward((3));
            }
            if (status4 == 2) {
                WZDJMainFrame.consoleTextArea.append("领取25天累计签到奖励\n");
                WZDJSender.rpcServerTotalSignInReward((4));
            }
        } else {
            for (Wzdj.TotalSignInRewardData totalSign : totalsigninrewarddatas) {
                Integer index = totalSign.getIndex();
                int status = totalSign.getRewardstatus() != null ? totalSign.getRewardstatus() : -1;
                String statusName = Consts.SignTotalState.nameById(status);
                sb.append(String.format("%s=%s(%s);", index, status, statusName));
            }
        }

        log.info(sb.toString());

        signindatas.forEach(signInData -> {
            Integer dayStatus = signInData.getStatus();
            if (dayStatus != null && dayStatus == 1) {
                WZDJSender.rpcServerSignInReward(signInData.getDay());
            }
        });
        totalsigninrewarddatas.forEach(totalSignInRewardData -> {
            Integer index = totalSignInRewardData.getIndex();
            Integer rewardstatus = totalSignInRewardData.getRewardstatus();
            if (rewardstatus != null && rewardstatus == 2) {//2待领取 1已领取
                WZDJSender.rpcServerTotalSignInReward((index));
            }
        });
    }


    //更新道具信息
    public static void itemUpdate(String itemId, long afterNum) {
        Stores.getInstance().getItemMap().put(itemId, afterNum);
        if ("gold".equals(itemId)) {
            WZDJMainFrame.goldJlabel.setText("金:" + afterNum);
        } else if ("diamond".equals(itemId)) {
            WZDJMainFrame.diamondJlabel.setText("钻:" + afterNum);
        } else if ("mythic_stone".equals(itemId)) {
            WZDJMainFrame.mythicStoneJlabel.setText("万能:" + afterNum);
        } else if ("mine_key".equals(itemId)) {
            WZDJMainFrame.mineKeyJlabel.setText("锤:" + afterNum);
        } else if ("recruit_key".equals(itemId)) {
            WZDJMainFrame.recruitKeyJlabel.setText("募卷:" + afterNum);
        } else if ("recruit_gem".equals(itemId)) {
            WZDJMainFrame.recruitGemJlabel.setText("募卷(高):" + afterNum);
        } else if ("energy".equals(itemId)) {
            WZDJMainFrame.energyJlabel.setText("体:" + afterNum);
            //log.info("体力更新:" + afterNum);
        }else {
            String msg = String.format("道具:%s(%s)更新数量:%s%n", itemId, Consts.Item.nameById(itemId), afterNum);
            log.info(msg);
            WZDJMainFrame.consoleTextArea.append(msg);
        }
    }

    //更新商店信息
    public static void shopUpdate(Wzdj.ShopInfo shopInfo) {
        //更新 商店 信息
        Stores.getInstance().getShopMap().put(shopInfo.getShopid(), shopInfo);

        //获取商品列表
        List<Wzdj.GoodsInfo> goodsInfoList = shopInfo.getGoodsinfo() != null ? shopInfo.getGoodsinfo() : new ArrayList<>();
        if (10001 == shopInfo.getShopid()) {

            Integer freshCnt = shopInfo.getLeftrefreshcnt() != null ? shopInfo.getLeftrefreshcnt() : 0;
            Integer maxFreshCnt = shopInfo.getMaxrefreshcnt() != null ? shopInfo.getMaxrefreshcnt() : 0;
            Stores.getInstance().getFreshCnt().set(freshCnt);

            int okCount = 0;
            for (int i = 0; i < goodsInfoList.size(); i++) {
                Wzdj.GoodsInfo goodsInfo = goodsInfoList.get(i);
                Integer goodsid = goodsInfo.getGoodsid() != null ? goodsInfo.getGoodsid() : 0;
                Integer buycnt = goodsInfo.getBuycnt() != null ? goodsInfo.getBuycnt() : 0;
                Integer maxBuyCnt = goodsInfo.getMaxbuycnt() != null ? goodsInfo.getMaxbuycnt() : 0;
                Integer salestate = goodsInfo.getSalestate() != null ? goodsInfo.getSalestate() : 0;
                Goods goods = Stores.getCfgGoods(shopInfo.getShopid(), goodsid);
                GoodsVO goodsVO = GoodsVO.of(shopInfo.getShopid(), goodsid, goods);

                //更新数据
                Stores.getInstance().getGoodsVOMap().put(i, goodsVO);
                //更新商品显示
                String goodsStr = String.format("%s(%s)|%s(%s)|%s", goodsVO.getRewardName(), goodsVO.getRewardNum(), goodsVO.getCostName(), goodsVO.getCostNum(), salestate);
                JLabel jLabel = WZDJMainFrame.goodLabels.get(i);
                jLabel.setText(goodsStr);

                if (salestate == 2) {
                    okCount++;
                }
            }
            String format = String.format("[%s/%s]", freshCnt, maxFreshCnt);
            WZDJMainFrame.shopRefreshBtn.setText("刷店" + format);


            if (freshCnt > 0 && okCount == 5) {//全部买完了，并且可刷新。就刷新
                if (Stores.getInstance().getFreshCnt().get() > 0) {//可刷新
                    WZDJSender.rpcServerShopRefresh(10001);
                }
            }


        } else if (shopInfo.getShopid() == 16001) {
            Wzdj.GoodsInfo adGoodsInfo = goodsInfoList.get(0);//广告 换体力

            Wzdj.GoodsInfo goodsInfo = goodsInfoList.get(1);//只关注钻石买体力
            Integer goodsid = goodsInfo.getGoodsid() != null ? goodsInfo.getGoodsid() : 0;
            Integer buycnt = goodsInfo.getBuycnt() != null ? goodsInfo.getBuycnt() : 0;
            Integer maxBuyCnt = goodsInfo.getMaxbuycnt() != null ? goodsInfo.getMaxbuycnt() : 0;
            Integer salestate = goodsInfo.getSalestate() != null ? goodsInfo.getSalestate() : 0;
            Goods goods = Stores.getCfgGoods(shopInfo.getShopid(), goodsid);
            GoodsVO goodsVO = GoodsVO.of(shopInfo.getShopid(), goodsid, goods);
            if (shopInfo.getShopid() == 16001 && goodsid == 2) {
                List<IdNum> costList = goods.getCost();
                IdNum idNum = costList.get(buycnt);
                goodsVO.setCostId(idNum.getId());
                goodsVO.setCostName("钻石");
                goodsVO.setCostNum(idNum.getNum() + "");
            }
            Stores.getInstance().getGoodsVOMap().put(5, goodsVO);
            //更新商品显示
            String goodsStr = String.format("%s(%s)|%s(%s)|%s", goodsVO.getRewardName(), goodsVO.getRewardNum(), goodsVO.getCostName(), goodsVO.getCostNum(), salestate);
            WZDJMainFrame.goodLabel7.setText(goodsStr);
        }

    }


    //关卡播报
    private static void waveChatMsg(int wave, int timeSecond) {
        List<Wzdj.FightFighterData> myFight5List = Stores.getInstance().getItFight5List();
        int yu = wave % myFight5List.size();
        Wzdj.FightFighterData fightFighterData = myFight5List.get(yu);
        String name = Consts.Fighter.nameById(fightFighterData.getFighterid());
        String waveInfo = String.format("关卡:%s,时间:%s秒,你的%s,才%s级", wave, timeSecond, name, fightFighterData.getLv());
        WZDJSender.rpcServerChatSend(waveInfo, 6);//局内聊天
    }


    public static void onFightMatchSuccess(Wzdj.RpcClientFightMatchSuccess rpcClientFightMatchSuccess){
        Stores.getInstance().setRpcClientFightMatchSuccess(rpcClientFightMatchSuccess);

        String ip = rpcClientFightMatchSuccess.getServer().getIp();
        int port = rpcClientFightMatchSuccess.getServer().getPort();
        int type = rpcClientFightMatchSuccess.getType() != null ? rpcClientFightMatchSuccess.getType() : 0;
        Stores.getInstance().setBattleType(type);
        String battleName = Consts.BattleType.nameById(type);
        log.info("匹配成功 战斗服ip:{},端口:{},模式:{}", ip, port, battleName);

        try {
            String wss = String.format("wss://%s:%s/xcore", rpcClientFightMatchSuccess.getServer().getIp(), rpcClientFightMatchSuccess.getServer().getPort());
            URI uri = new URI(wss);
            EventLoopGroup group = new NioEventLoopGroup();
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<Channel>() {
                        @Override
                        public void initChannel(Channel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            if ("wss".equalsIgnoreCase(uri.getScheme())) {
                                SslContext sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
                                pipeline.addLast(sslCtx.newHandler(ch.alloc()));
                            }
                            // 创建HttpRequest，并设置为WebSocket升级的请求
                            FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, wss);
                            // 设置HTTP头
                            HttpHeaders headers = request.headers();
                            headers.add(HttpHeaderNames.HOST, uri.getHost());
                            headers.add(HttpHeaderNames.ORIGIN, "https://wzdj.hnys68.cn:20211");
                            headers.add(HttpHeaderNames.UPGRADE, "websocket");
                            headers.add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE);
                            headers.add(HttpHeaderNames.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090c11)XWEB/11275");
//                            headers.add("Sec-Websocket-Key", "AU9v7EGqfFibu2f4ksyboQ==");
                            pipeline.addLast(new HttpClientCodec());
                            pipeline.addLast(new HttpObjectAggregator(8192));
                            pipeline.addLast(new ChunkedWriteHandler());
                            pipeline.addLast(WebSocketClientCompressionHandler.INSTANCE);
                            pipeline.addLast(new WebSocketClientProtocolHandler(WebSocketClientHandshakerFactory.newHandshaker(uri, WebSocketVersion.V13, "binary", true, headers), true));
                            pipeline.addLast(new WZDJPackWSEncoder());
                            pipeline.addLast(new WZDJPackWSDecoder());
                            pipeline.addLast(new WZDJWSClientHandler());
                        }
                    });
            Channel tmpChannel = b.connect(uri.getHost(), uri.getPort()).sync().channel();
            Stores.getInstance().setFightChannel(tmpChannel);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void onFightLoginFinish(Wzdj.RpcClientFightLoginFinish rpcClientFightLoginFinish){
        Stores.getInstance().getIsFight().set(true);
        fightShowReset();
    }

    //战斗连接结果
    public static void onFightEnd(Wzdj.RpcClientFightEnd rpcClientFightEnd){
        Stores.getInstance().getIsFight().set(false);
    }

    //大厅连接结果
    public static void onFightResult(Wzdj.RpcClientFightCoopResult rpcClientFightCoopResult){
        Stores.getInstance().getIsFight().set(false);
        //战斗结果 数据整理
        int reason = rpcClientFightCoopResult.getReason() != null ? rpcClientFightCoopResult.getReason() : 0;
        String reasonName = Consts.EndReason.nameById(reason);
        int wave = rpcClientFightCoopResult.getWave() != null ? rpcClientFightCoopResult.getWave() : 0;
        int maxwave = rpcClientFightCoopResult.getMaxwave()!= null ? rpcClientFightCoopResult.getMaxwave() : 0;
        Long score = rpcClientFightCoopResult.getScore() != null ? rpcClientFightCoopResult.getScore() : 0;
        List<Wzdj.FightResultPlayerData> players = rpcClientFightCoopResult.getPlayers();
        long myDmg = 0;
        long itDmg = 0;
        long sumDmg = 0;
        for (Wzdj.FightResultPlayerData player : players) {
            Integer uid = player.getStat().getUid() != null ? player.getStat().getUid() : 0;
            long dmg = player.getStat().getTotaldmg() != null ? player.getStat().getTotaldmg() : 0;
            if (Objects.equals(uid, Stores.getInstance().getMyUid())) {
                myDmg = dmg;
            } else {
                itDmg = dmg;
            }
            sumDmg += dmg;
        }
        long myPercent = (myDmg * 100 / sumDmg) ;
        long itPercent = (itDmg * 100 / sumDmg) ;

        int popuNum = Stores.getInstance().getMyPopuNum();
        String myBase = WZDJService.getUBase(Stores.getInstance().getBasicInfo());
        String itBase = WZDJService.getUBase(Stores.getInstance().getItBasicInfo());
        String myDmgShow = String.format("%s:%s伤(%s)分%s,", myBase, numberWE(myDmg), myPercent + "%", numberWE(score));
        String itDmgShow = String.format("%s:%s伤(%s)",  itBase, numberWE(itDmg), itPercent + "%");
        int battleType = Stores.getInstance().getBattleType();
        String battleName = Consts.BattleType.nameById(battleType);
        String fightResult = String.format("战斗结束:%s,关:%s,怪:%s,%s,%s",
                battleName + "(" + reasonName + ")",
                wave + "/" + maxwave,
                popuNum,
                myDmgShow,
                itDmgShow);
        //打印和展示
        log.info(fightResult);
        WZDJMainFrame.consoleTextArea.append(DateUtil.formatDateTime(new Date()) + fightResult + "\n");
        //重置战斗状态
        fightStateReset();
    }

    public static String numberWE(long score) {
        if (score < 10000) {
            return String.valueOf(score);
        } else if (score < 100000000) {
            return String.format("%.1f", (score / 10000F)) + "万";
        } else {
            return String.format("%.1f", (score / 100000000F)) + "亿";
        }
    }

    //战斗数据清理
    public static void fightStateReset(){
        Stores.getInstance().getMyActorMap().clear();
        Stores.getInstance().getMyGridMap().clear();
        Stores.getInstance().getItActorMap().clear();
        Stores.getInstance().getItGridMap().clear();
        Stores.getInstance().setBattleType(0);
        Stores.getInstance().setWave(0);
        Stores.getInstance().setMyFood(0);
        Stores.getInstance().setMyLuckCoin(0);
        Stores.getInstance().setMyPopuNum(0);
        Stores.getInstance().setLastFromGrid(-1);
        Stores.getInstance().setLastToGrid(-1);
        Stores.getInstance().getMyFight5List().clear();
        Stores.getInstance().getItFight5List().clear();
    }

    public static void fightShowReset(){
        //reset 表现
        WZDJMainFrame.playPanel.clear();
        WZDJMainFrame.callCostJlabel.setText("召唤需要:0");
        WZDJMainFrame.enhance1Jlabel.setText("强蓝色lv:0");
        WZDJMainFrame.enhance2Jlabel.setText("强紫色lv:0");
        WZDJMainFrame.enhance3Jlabel.setText("强金橙lv:0");
        WZDJMainFrame.enhance4Jlabel.setText("召概率lv:0");
        WZDJMainFrame.myTextArea.setText("");
        WZDJMainFrame.otherTextArea.setText("");
    }


    //协议耗时打印
    public static void printTimeLog() {
        List<StatVO> statVOList = new ArrayList<>();
        for (Map.Entry<String, CopyOnWriteArrayList<Long>> en : WZDJCaller.methodTimeMap.entrySet()) {
            String methodName = en.getKey();
            CopyOnWriteArrayList<Long> timeList = en.getValue();
            long sum = timeList.stream().mapToLong(Long::longValue).sum();
            double average = timeList.stream().mapToLong(Long::longValue).average().orElse(0);
            StatVO statVO = new StatVO();
            statVO.setAvg(average);
            statVO.setName(methodName);
            statVO.setSum(sum);
            statVO.setTimeList(timeList);
            statVOList.add(statVO);
        }
        statVOList.sort(Comparator.comparingDouble(StatVO::getSum).reversed());
        int limit = Math.min(statVOList.size(), 10);//打印耗时前10即可
        System.out.println("打印协议耗时统计信息");
        for (int i = 0; i < limit; i++) {
            int rank = i + 1;//耗时排名
            StatVO statVO = statVOList.get(i);
            System.out.printf("[%s] 次数:%s,总耗时:%s,平均耗时:%.2s毫秒: %s%n",
                    rank, statVO.getTimeList().size(), DateUtils.secondToTime(statVO.getSum() / 1000),
                    statVO.getAvg(), statVO.getName());
        }
    }


    //cron定时任务-------------------------------------------------------------------------------------------------------

    //更新 数显
    public static void jobTime() {
        String currTimeStr = DateUtil.formatDateTime(new Date());
        WZDJMainFrame.currTimeJLabel.setText(currTimeStr);
    }

    //更新 体力
    public static void jobEnergy() {
        //大厅刷新
        if (Stores.getInstance().getHomeChannel() == null) {
            return;
        }
        int next = Stores.getInstance().getEnergyNext().decrementAndGet();
        if (next <= 0) {
            log.info("恢复一点体力");
            if (Stores.getInstance().getEnergy().get() < 30){//<30 体力恢复1点
                Stores.getInstance().getEnergy().incrementAndGet();
            }
            Stores.getInstance().getEnergyNext().set(10 * 60);//10分钟恢复一点
        }
        WZDJMainFrame.energyJlabel.setText("体力:" + Stores.getInstance().getEnergy().get());
        WZDJMainFrame.energyNextJlabel.setText("刷新:" + DateUtils.secondToTime(Stores.getInstance().getEnergyNext().get()));
    }

    //更新 关卡倒计时
    public static void jobWaveTime() {
        if (!Stores.getInstance().getIsFight().get()) {
            return;
        }
        //更新 关卡倒计时:
        int waveTime = Stores.getInstance().getWaveTime().getAndDecrement();//建议秒
        if (waveTime > 0) {
            String waveTimeStr = DateUtils.secondToTime(waveTime);
            WZDJMainFrame.timeLabel.setText(waveTimeStr);
        }
    }


    public static void jobCheckQRExpire() {
        if (Stores.getInstance().isQrExpired()) {//二维码失效了，直接不处理
            return;
        }
        if (StrUtil.isBlank(Stores.getInstance().getQrUuid())) {//qrUuid为空，不处理
            log.error("qrUuid为空不处理");
            return;
        }
        WxCheckQrRes wxCheckQrRes = WxApi.checkQr(Stores.getInstance().getQrUuid());
        if (wxCheckQrRes == null) {
            log.error("checkQr has error wxCheckQrRes is null");
            return;
        }
        log.info(JSON.toJSONString(wxCheckQrRes));
        //扫码 结果处理
        if (wxCheckQrRes.getCode() == 0 && wxCheckQrRes.isSuccess() && wxCheckQrRes.getData().containsKey("acctSectResp")) {
            WxCheckQrScanOkData wxCheckQrScanOkData = JSON.parseObject(JSON.toJSONString(wxCheckQrRes.getData()), WxCheckQrScanOkData.class);
            String format = String.format("%s  登陆成功", wxCheckQrScanOkData.getAcctSectResp().getNickName());
            WZDJMainFrame.qrStatusLabel.setText(format);
            log.info(format);
            Stores.getInstance().setQrExpired(true);//二维码失效
            //重新加载选项框
            String wxAlias = wxCheckQrScanOkData.getAcctSectResp().getAlias();
            String selectItemStr = Consts.LOGIN_TYPE_WX + wxAlias;
            //获取老的数据
            WZDJLocalItem wzdjLocalItem = Stores.localDO.getLoginMap().getOrDefault(selectItemStr, new WZDJLocalItem());
            //更新内存数据
            wzdjLocalItem.setLoginType(Consts.LOGIN_TYPE_WX);
            wzdjLocalItem.setItemSelectStr(selectItemStr);//key更新
            wzdjLocalItem.setWxAccountInfo(wxCheckQrScanOkData);//变动数据
            Stores.localDO.getLoginMap().put(selectItemStr, wzdjLocalItem);//回写内存数据
            //回写磁盘
            FileUtil.writeString(JSONUtil.toJsonPrettyStr(Stores.localDO), Stores.localFile, "UTF-8");//回写磁盘
            //选项卡更新
            WZDJMainFrame.loadAccountSelect();
            WZDJMainFrame.accountBox.setSelectedItem(selectItemStr);
        } else if (wxCheckQrRes.getCode() == 0 && wxCheckQrRes.isSuccess() && wxCheckQrRes.getMessage().equals("成功")) {
            WxCheckQrScanData wxCheckQrScanData = JSON.parseObject(JSON.toJSONString(wxCheckQrRes.getData()), WxCheckQrScanData.class);
            int expiredTime = wxCheckQrScanData.getExpiredTime();
            String uuid = wxCheckQrScanData.getUuid();//uuid
            String nickName = wxCheckQrScanData.getNickName();//昵称,仅在扫码时有值
            String headImgUrl = wxCheckQrScanData.getHeadImgUrl();//头像可访问,仅在扫码时有值
            //http://wx.qlogo.cn/mmhead/ver_1/X3z4HyW8Yz4dCEfW1lgaTmwbtINO2icXP7FibfyP1UmG1y6DxFg1U1KFwzyUJFiboIu1cWv84XSzIw2qiaV7ESVrLN4bOU0ZXFeNTfwmfZQWlcM/0
            if (wxCheckQrScanData.getStatus() == 0) {
                Stores.getInstance().setQrExpired(false);
                WZDJMainFrame.qrStatusLabel.setText(String.format("二维码有效期%s秒", expiredTime));
            } else if (wxCheckQrScanData.getStatus() == 1) {
                Stores.getInstance().setQrExpired(false);
                WZDJMainFrame.qrStatusLabel.setText(String.format("%s登录中,请在%s秒内确认", nickName, expiredTime));
            } else {//未知状态
                log.error("未知的状态:" + wxCheckQrScanData.getStatus() + ",json:" + JSON.toJSONString(wxCheckQrRes));
                Stores.getInstance().setQrExpired(true);
                WZDJMainFrame.qrStatusLabel.setText("码已失效,点击刷新");
            }
        } else {

        }

    }

    public static void jobAutoFight() {
        if (!Stores.getInstance().getIsFight().get() || Stores.getInstance().getFightChannel() == null) {//非战斗状态不处理
            return;
        }
        //自动召唤
        if (Stores.getInstance().isAutoCall() && !Stores.getInstance().isFightFull()) {
            if (Stores.getInstance().getWave() > 45 && Stores.getInstance().getMyFood() > Stores.getInstance().getCallCost()) {//够召唤才召唤
                WZDJSender.rpcServerFightFighterCall();//关卡45之后 定时任务召唤 前期攒一攒钱
            }
        }
        //自动3合1
        if (Stores.getInstance().isAutoCombination()) {
            WZDJService.checkCombination();
        }
        //自动合成神话
        if (Stores.getInstance().isAutoMythic()) {
            WZDJService.checkMythic();
        }
        //自动移动
        if (Stores.getInstance().isAutoMove()) {
            WZDJService.checkMove();
        }
        //自动移除
        if (Stores.getInstance().isAutoRemove()) {
            WZDJService.checkRemove();
        }
        //自动赌博
        if (Stores.getInstance().isAutoGambling() && Stores.getInstance().getMyLuckCoin() > 0) {
            if (Stores.getInstance().getWave() > 0) {
                if (!Stores.getInstance().isFightFull()) {
                    if (Stores.getInstance().getWave() < 30) {
                        for (int i = 0; i < Stores.getInstance().getMyLuckCoin(); i++) {
                            ThreadUtil.sleep(200);
                            WZDJSender.rpcServerFightBet(3);//自动打赌紫色
                        }
                    } else if (Stores.getInstance().getWave() < 60) {
                        ThreadUtil.sleep(200);
                        WZDJSender.rpcServerFightBet(3);//自动打赌紫色
                    }
                } else {
                    //System.out.println("已满员,放弃赌博");
                }
            }
        }
        //自动强化
        if (Stores.getInstance().isAutoStrengthen()) {
            //25关 后要求必须强化到12级
            if (Stores.getInstance().getWave() > 15 && Stores.getInstance().getEnhance4().get() < 12) {
                WZDJSender.rpcServerFightEnhance(4);//强化召唤概率
            }
            //40关 后才强化金色
            if (Stores.getInstance().getWave() > 40) {
                int level = Stores.getInstance().getEnhance3().get();
                int needLuckCoin = level + 1;
                if (Stores.getInstance().getMyLuckCoin() >= needLuckCoin) {//能升级就升级
                    WZDJSender.rpcServerFightEnhance(3);//强化金色和神话伤害
                }
            }

        }
        //由关卡 开始控制 更合理
//        volatile boolean autoStrengthen = true;    //自动强化
//        volatile boolean autoHunting = true;       //自动狩猎
    }


    //开始游戏
    public static void startFight(int battleType) {
        String battleName = Consts.BattleType.nameById(battleType);
        int cost = 5;
        if (battleType == 3) {
            cost = 3;
        }
        String fightStart = String.format("开始战斗:%s,消耗:%s体力", battleName, cost);
        log.info(fightStart);
        WZDJMainFrame.consoleTextArea.append(DateUtil.formatTime(new Date()) + fightStart + "\n");
        WZDJSender.rpcServerFightMatch(battleType);
    }

    //开始游戏
    public static void jobStartFight() {
        if (Stores.getInstance().getHomeChannel() == null || !Stores.getInstance().isLogin()) {
            return;
        }
        boolean isFight = Stores.getInstance().getIsFight().get();
        int model = Stores.getInstance().getModel().get();

        if (!isFight && model ==1) {
            log.info("战斗状态:{},当前model:{},体力:{}", isFight, model, Stores.getInstance().getEnergy().get());

            DHWzdjCfg dhWzdjCfg = Stores.getInstance().getDhWzdjCfg();
            //等级
            Integer lv = Stores.getInstance().getBasicInfo().getLv() != null ? Stores.getInstance().getBasicInfo().getLv() : 0;

            //pvp状态
            int dayPass1Status = Stores.getInstance().getDayPass1().get();
            int dayPass2Status = Stores.getInstance().getDayPass2().get();
            int dayPass5Status = Stores.getInstance().getDayPass5().get();

            int win = Stores.getInstance().getDayPvpWin().get();
            int need = Stores.getInstance().getDayPvpNeed().get();
            int reward = Stores.getInstance().getDayPvpReward().get();


            //选择挂机模式
            //判断能不能进入 PVP 模式
            if (dhWzdjCfg.isPvpOpen() && need >0 &&  win < need && Stores.getInstance().getEnergy().get() >= 3){
                startFight(4);//竞技
                return;
            }
            //普通模式
            if (Stores.getInstance().getEnergy().get() >= 5) {
                if (dayPass1Status == 0){
                    startFight(1);//普通
                } else if (dayPass2Status == 0 && lv > 15) {//每日 首通 困难
                    startFight(2);//困难
                }
//                else if (dayPass5Status == 0 && lv > 20) {//每日 首通 地狱  地狱太难了，还是手动通关吧
//                    startFight(5);//地狱
//                }
                else if (lv > 22) {//22级 以上才开始困难
                    startFight(2);//困难
                }
                else {
                    startFight(1);//普通
                }
            }


        }
    }
                /*
                    50|70|100|125|150|(10个)200|
                    恢复
                    一天最多15次体力 15 * 15 = 225体力
                    10分钟恢复一点体力 一小时恢复6点体力 24小时恢复 144点体力
                    一天总体力数: 225+114=369
                    一天封顶可玩: 369/5=73局游戏
                    消耗
                    一局差不多9-22分钟之间
                    求平均按15分钟一局   1小时4局  24*4=96局
                    保守10分钟一局       1小时6局  24*6=144局
                    所以说体力还是不够用的。
                 */


    public static void jobHomeChat() {
        if (Stores.getInstance().getHomeChannel() != null && Stores.getInstance().isHomeAutoChat()) {
            String text = WZDJMainFrame.homeChatInput.getText();
            if (StrUtil.isNotBlank(text)) {
                System.out.println("大厅发送文本:" + text);
                WZDJSender.rpcServerChatSend(text, 1);//大厅定时发送聊天
            }
        }
    }


    public static void jobHomeChannel() {
        if (Stores.getInstance().getHomeChannel() == null) {
            String time = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
            WZDJMainFrame.consoleTextArea.append(time + "整点05分 检查登录不在线,尝试重连大厅\n");
            Stores.getWzdjMainFrame().handleConnClick();
        } else {
            String time = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
            WZDJMainFrame.consoleTextArea.append(time + "整点05分 检查登录状态正常\n");
        }
    }


    public static void jobFightUI() {
        if (!Stores.getInstance().getIsFight().get()) {
            return;
        }
        long start = System.currentTimeMillis();
        //我的信息
        WZDJMainFrame.myFoodLabel.setText("食物:" + Stores.getInstance().getMyFood());
        WZDJMainFrame.myLuckCoinLabel.setText("幸运:" + Stores.getInstance().getMyLuckCoin());
        WZDJMainFrame.monsterLabel.setText(String.format("怪:%s/%s", Stores.getInstance().getMonsterNum(), Stores.getInstance().getMonsterLimit()));
        WZDJMainFrame.myPopuLabel.setText("英雄:" + Stores.getInstance().getMyPopuNum() + "/" + Stores.getInstance().getMyPopuLimit());
        //更新关卡倒计时
        jobWaveTime();
        //对方的信息
        WZDJMainFrame.itFoodLabel.setText("it食物:" + Stores.getInstance().getItFood());
        WZDJMainFrame.itLuckCoinLabel.setText("it幸运:" + Stores.getInstance().getItLuckCoin());
        WZDJMainFrame.itPopuLabel.setText("it英雄:" + Stores.getInstance().getItPopuNum() + "/" + Stores.getInstance().getItPopuLimit());
        //战斗区域
        WZDJMainFrame.playPanel.update(Stores.getInstance().getMyGridMap(), true);//定时更新自己
        WZDJMainFrame.playPanel.update(Stores.getInstance().getItGridMap(), false);//定时更新对面
    }

}
